package com.basker.pisces.core.resource;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.CollectionUtils;

import com.basker.pisces.exception.ExceptionFactory;
import com.basker.pisces.resource.StringResources;
import com.basker.pisces.spring.SpringApplicationContext;

public class ResourceLoaderManager implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(ResourceLoaderManager.class);

    public static void load() {
        load(new String[0]);
    }

    public static void load(IResourceLoader... loaders) {
        ResourceLoaderManager loder = new ResourceLoaderManager(Arrays.asList(loaders));
        loder.load0();
    }

    public static void load(String... beanNames) {
        List<IResourceLoader> supportResources = null;

        if (beanNames == null || beanNames.length == 0) {
            supportResources = SpringApplicationContext.getInstance().getBeans(IResourceLoader.class);
        } else {
            supportResources = Stream.of(beanNames)
                    .map(beanName -> SpringApplicationContext.getInstance().getBean(beanName, IResourceLoader.class))
                    .collect(Collectors.toList());

        }

        ResourceLoaderManager loder = new ResourceLoaderManager(supportResources);
        loder.load0();
    }

    @Autowired
    private List<IResourceLoader> supportResources;

    @Autowired
    private ResourcePatternResolver resolver;

    public ResourceLoaderManager() {

    }

    private ResourceLoaderManager(List<IResourceLoader> supportResources) {
        this.supportResources = supportResources;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        if (CollectionUtils.isEmpty(this.supportResources)) {
            return;
        }

        this.load0();
    }

    /**
     * 开始加载配置文件
     */
    private void load0() {
        // 按优先级排序，大的优先
        this.sort();

        for (IResourceLoader loader : supportResources) {
            String name = loader.getClass().getName();
            String msg = StringResources.get("pisces-core.ResourceLoaderManager.load0-001", name);
            if (msg == null) {
                msg = MessageFormat.format("ResouceLoader:{0} is loading......", name);
            }

            if (logger.isInfoEnabled()) {
                logger.info(msg);
            }

            try {
                List<Resource> resourceList = null;
                String[] locationPatterns = loader.getLocationPatterns();

                if (!ArrayUtils.isEmpty(locationPatterns)) {
                    Predicate<Resource> filter = loader.getFilter();

                    for (String locationPattern : locationPatterns) {
                        msg = StringResources.get("pisces-core.ResourceLoaderManager.load0-002", locationPattern);
                        if (msg == null) {
                            msg = MessageFormat.format("Loacation:{0} is scaning......", name);
                        }
                        if (logger.isInfoEnabled()) {
                            logger.info(msg);
                        }

                        Resource[] resources = resolver.getResources(locationPattern);

                        if (ArrayUtils.isEmpty(resources)) {
                            if (logger.isWarnEnabled()) {
                                logger.warn(StringResources.get("pisces-core.ResourceLoaderManager.load0-003",
                                        locationPattern));
                            }
                        } else {
                            if (resourceList == null) {
                                resourceList = new ArrayList<>(resources.length);
                            }

                            msg = StringResources.get("pisces-core.ResourceLoaderManager.load0-004", locationPattern);
                            if (msg == null) {
                                msg = MessageFormat.format("Find the following resources in {0}:", locationPattern);
                            }

                            if (logger.isInfoEnabled()) {
                                logger.info(msg);
                            }

                            for (Resource resource : resources) {
                                if (filter != null && !filter.test(resource)) {
                                    continue;
                                }

                                if (logger.isInfoEnabled()) {
                                    logger.info(resource.getURL().toString());
                                }

                                resourceList.add(resource);
                            }
                        }
                    }
                }

                if (resourceList == null) {
                    loader.load(new Resource[0]);
                } else {
                    loader.load(resourceList.toArray(new Resource[resourceList.size()]));
                }

                if (logger.isInfoEnabled()) {
                    logger.info(StringResources.get("pisces-core.ResourceLoaderManager.load0-005", name));
                }
            } catch (Exception e) {
                if (logger.isErrorEnabled()) {
                    logger.error(
                            StringResources.get("pisces-core.ResourceLoaderManager.load0-006", name, e.getMessage()),
                            e);
                }

                throw ExceptionFactory.createRuntimeException(e);
            }

        }
    }

    private void sort() {
        Collections.sort(supportResources, new Comparator<IResourceLoader>() {

            @Override
            public int compare(IResourceLoader o1, IResourceLoader o2) {
                Integer priority1 = o1.getPriority();
                Integer priority2 = o2.getPriority();

                priority1 = priority1 != null ? priority1 : 0;
                priority2 = priority2 != null ? priority2 : 0;

                return -(priority1.compareTo(priority2));
            }
        });
    }

}
